package com.derbysoft.lowcostadapter.dswitch2;

import com.derbysoft.lowcostadapter.commons.log.LogBody;
import com.derbysoft.lowcostadapter.commons.log.LogUtils;
import com.derbysoft.lowcostadapter.commons.log.PerformanceLogger;
import com.derbysoft.lowcostadapter.commons.log.TransactionLogger;
import com.derbysoft.lowcostadapter.commons.model.OperationType;
import com.derbysoft.lowcostadapter.commons.redis.service.SystemVariableCacheService;
import com.derbysoft.lowcostadapter.commons.repository.ReservationLogRepository;
import com.derbysoft.lowcostadapter.commons.repository.SystemVariableRepository;
import com.derbysoft.lowcostadapter.commons.repository.TransactionLogRepository;
import com.derbysoft.lowcostadapter.commons.utils.DateTimeUtils;
import com.derbysoft.lowcostadapter.dswitch2.handler.RemoteServiceHandler;
import com.derbysoft.lowcostadapter.dswitch2.handler.ReservationIDUtils;
import com.derbysoft.remote.dswitch.core.*;
import com.derbysoft.remote.dswitch.generic.constants.TransactionActionType;
import com.derbysoft.remote.dswitch.hotel.adapter.hotelprovider.HotelProviderWebService;
import com.derbysoft.remote.dswitch.hotel.dto.*;
import com.derbysoft.remote.dswitch.hotel.dto.sync.availstatuschange.SyncAvailStatusChangeRQ;
import com.derbysoft.remote.dswitch.hotel.dto.sync.availstatuschange.SyncAvailStatusChangeRS;
import com.derbysoft.remote.dswitch.hotel.dto.sync.ratechange.SyncRateChangeRQ;
import com.derbysoft.remote.dswitch.hotel.dto.sync.ratechange.SyncRateChangeRS;
import com.derbysoft.remote.dswitch.hotel.dto.sync.rateplanchange.SyncRatePlanChangeRQ;
import com.derbysoft.remote.dswitch.hotel.dto.sync.rateplanchange.SyncRatePlanChangeRS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.concurrent.Executor;

/**
 * Created by: jason
 * Date: 2012-06-01
 */
@Service("ds2HotelProviderWebService")
public class HotelProviderServiceProxy implements HotelProviderWebService {

    @Override
    public GenericRS<HotelAvailRS> getAvailability(GenericRQ<HotelAvailRQ> request) {
        PerformanceLogger.init();
        GenericRS<HotelAvailRS> response = availabilityHandler.handle(request);

        saveAvailPerformanceLog(request, response);

        saveSessionLog();

        return response;
    }

    @Override
    public GenericRS<HotelReservationRS> makeReservation(GenericRQ<HotelReservationRQ> request) {

        GenericRS<HotelReservationRS> response;

        PerformanceLogger.init();
        if (request.getPayload().getResActionType() == TransactionActionType.BOOK) {
            PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_RESERVATION.name());
            response = ds2HotelReservationHandler.handle(request);
        } else {
            PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.UNSUPPORTED_OPERATION.name());
            response = createUnsupportedError(request);
        }

        saveReservationPerformanceLog(request, response);

        saveSessionLog();

        return response;
    }

    @Override
    public GenericRS<CancelRS> cancelReservation(GenericRQ<CancelRQ> request) {
        GenericRS<CancelRS> response = ds2HotelCancelHandler.handle(request);

        saveCancellationPerformanceLog(request, response);

        saveSessionLog();

        return response;
    }

    private GenericRS<HotelReservationRS> createUnsupportedError(GenericRQ<HotelReservationRQ> request) {
        GenericRS<HotelReservationRS> reservationRS;
        reservationRS = new GenericRS<HotelReservationRS>();
        ResponseHeader responseHeader = new ResponseHeader();
        responseHeader.setTaskId(request.getHeader().getTaskId());
        reservationRS.setHeader(responseHeader);
        ErrorsDTO errors = new ErrorsDTO();
        ArrayList<ErrorDTO> errorDTOs = new ArrayList<ErrorDTO>();
        ErrorDTO errorDTO = new ErrorDTO();
        errorDTO.setCode("System");
        errorDTO.setSource(adapter);
        String message = String.format("Unsupported operation [%s]", TransactionActionType.INITIATE);
        errorDTO.setMessage(message);
        errorDTOs.add(errorDTO);
        errors.setErrorList(errorDTOs);
        reservationRS.setErrors(errors);
        return reservationRS;
    }

    private void saveSessionLog() {
        LogUtils.saveLogs(executor, systemVariableCacheService,
                systemVariableRepository, transactionLogRepository, reservationLogRepository);
    }

    private void saveCancellationPerformanceLog(GenericRQ<CancelRQ> request, GenericRS<CancelRS> response) {
        PerformanceLogger.init();
        PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.CANCELLATION.name());
        setPerformanceLogHeader(request);
        PerformanceLogger.append(LogBody.ERSP_NUMBER, ReservationIDUtils.getErspNo(request.getPayload().getHotelReservationIDs()).getResIDValue());

        if (response.getErrors() != null) {
            TransactionLogger.getReservationLogger().get().setError(true);
            TransactionLogger.getReservationLogger().get().setException(response.getErrors().getErrorList().get(0).getMessage());
            PerformanceLogger.append(LogBody.PROCESSED_RESULT, LogBody.ProcessResult.Fail.name());
        } else {
            PerformanceLogger.append(LogBody.PROCESSED_RESULT, LogBody.ProcessResult.Success.name());
        }

        PerformanceLogger.save(executor);
    }


    private void saveReservationPerformanceLog(GenericRQ<HotelReservationRQ> request, GenericRS<HotelReservationRS> response) {
        ReservationRoomStayDTO reservationRoomStayDTO = request.getPayload().getHotelReservation().getRoomStays().get(0);
        setPerformanceLogHeader(request);
        PerformanceLogger.append(LogBody.DERBY_HOTEL_CODES, request.getPayload().getHotelReservation().getHotelRef().getCode());
        PerformanceLogger.append(LogBody.CHECK_IN, DateTimeUtils.formatDate(reservationRoomStayDTO.getStayDateRange().getCheckInDate()));
        PerformanceLogger.append(LogBody.CHECK_OUT, DateTimeUtils.formatDate(reservationRoomStayDTO.getStayDateRange().getCheckOutDate()));
        PerformanceLogger.append(LogBody.RATE_PLAN, reservationRoomStayDTO.getRatePlanCode());
        PerformanceLogger.append(LogBody.ROOM_TYPE, reservationRoomStayDTO.getRoomTypeCode());
        PerformanceLogger.append(LogBody.NUMBER_OF_UNIT, String.valueOf(reservationRoomStayDTO.getNumberOfUnits()));
        PerformanceLogger.append(LogBody.ADULT, String.valueOf(reservationRoomStayDTO.getAdultCount()));
        PerformanceLogger.append(LogBody.CHILD, String.valueOf(reservationRoomStayDTO.getChildCount()));
        PerformanceLogger.append(LogBody.ERSP_NUMBER, ReservationIDUtils.getErspNo(request.getPayload().getHotelReservationIDs()).getResIDValue());

        if (response.getErrors() != null) {
            TransactionLogger.getReservationLogger().get().setError(true);
            TransactionLogger.getReservationLogger().get().setException(response.getErrors().getErrorList().get(0).getMessage());
            PerformanceLogger.append(LogBody.PROCESSED_RESULT, LogBody.ProcessResult.Fail.name());
        } else {
            PerformanceLogger.append(LogBody.PROCESSED_RESULT, LogBody.ProcessResult.Success.name());
        }

        PerformanceLogger.save(executor);
    }


    private void saveAvailPerformanceLog(GenericRQ<HotelAvailRQ> request, GenericRS<HotelAvailRS> response) {

        PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_AVAIL.name());
        setPerformanceLogHeader(request);
        PerformanceLogger.append(LogBody.DERBY_HOTEL_CODES, getHotelCodes(request.getPayload().getAvailCriteria()));
        PerformanceLogger.append(LogBody.CHECK_IN, DateTimeUtils.formatDate(request.getPayload().getAvailCriteria().getStayDateRange().getCheckInDate()));
        PerformanceLogger.append(LogBody.CHECK_OUT, DateTimeUtils.formatDate(request.getPayload().getAvailCriteria().getStayDateRange().getCheckOutDate()));
        PerformanceLogger.append(LogBody.NUMBER_OF_UNIT, String.valueOf(request.getPayload().getAvailCriteria().getRoomStayCandidates().getNumberOfUnits()));
        PerformanceLogger.append(LogBody.ADULT, String.valueOf(request.getPayload().getAvailCriteria().getRoomStayCandidates().getAdultCount()));
        PerformanceLogger.append(LogBody.CHILD, String.valueOf(request.getPayload().getAvailCriteria().getRoomStayCandidates().getChildCount()));

        if (response.getErrors() != null) {
            TransactionLogger.getAvailLogger().get().setError(true);
            TransactionLogger.getAvailLogger().get().setException(response.getErrors().getErrorList().get(0).getMessage());
            PerformanceLogger.append(LogBody.PROCESSED_RESULT, LogBody.ProcessResult.Fail.name());
        } else {
            PerformanceLogger.append(LogBody.PROCESSED_RESULT, LogBody.ProcessResult.Success.name());
        }
        PerformanceLogger.save(executor);
    }

    private void setPerformanceLogHeader(GenericRQ request) {
        PerformanceLogger.append(LogBody.TASK_ID, request.getHeader().getTaskId());
        PerformanceLogger.append(LogBody.CHANNEL, request.getHeader().getSource().getUniqueID().getId());
        PerformanceLogger.append(LogBody.PROVIDER_CHAIN, "LowCostBeds");
    }

    private String getHotelCodes(HotelAvailCriteriaDTO availCriteria) {
        return availCriteria.getHotelRef().getCode();
    }


    @Value("${dswitch2.adapter}")
    private String adapter;

    @Autowired
    @Qualifier("ds2HotelAvailabilityHandler")
    private RemoteServiceHandler<GenericRQ<HotelAvailRQ>, GenericRS<HotelAvailRS>> availabilityHandler;

    @Autowired
    @Qualifier("ds2HotelReservationHandler")
    private RemoteServiceHandler<GenericRQ<HotelReservationRQ>, GenericRS<HotelReservationRS>> ds2HotelReservationHandler;

    @Autowired
    @Qualifier("ds2HotelCancelHandler")
    private RemoteServiceHandler<GenericRQ<CancelRQ>, GenericRS<CancelRS>> ds2HotelCancelHandler;

    @Autowired
    private TransactionLogRepository transactionLogRepository;

    @Autowired
    private ReservationLogRepository reservationLogRepository;

    @Autowired
    @Qualifier("taskExecutor")
    private Executor executor;

    @Autowired
    private SystemVariableCacheService systemVariableCacheService;

    @Autowired
    private SystemVariableRepository systemVariableRepository;


    @Override
    public GenericRS<HotelMultiAvailRS> getMultiAvailability(GenericRQ<HotelMultiAvailRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<CancelNotifyRS> notifyCancel(GenericRQ<CancelNotifyRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<HotelMultiRateRangeRS> getMultiRateRange(GenericRQ<HotelMultiRateRangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<HotelRateRangeRS> getRateRange(GenericRQ<HotelRateRangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<HotelReservationNotifyRS> notifyReservation(GenericRQ<HotelReservationNotifyRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<SyncAvailStatusChangeRS> syncAvailStatusChange(GenericRQ<SyncAvailStatusChangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<SyncRateChangeRS> syncRateChange(GenericRQ<SyncRateChangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<SyncRatePlanChangeRS> syncRatePlanChange(GenericRQ<SyncRatePlanChangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<QueryHotelReservationAuditRS> query(GenericRQ<QueryHotelReservationAuditRQ> queryRQ) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<QueryReservationTransactionStatusRS> queryReservationTransactionStatus(GenericRQ<QueryReservationTransactionStatusRQ> queryRQ) {
        throw new UnsupportedOperationException();
    }
}
